//
//  vector4.cpp
//  GLDemo
//
//  Created by wneqi on 14-6-6.
//  Copyright (c) 2014年 xiewneqi. All rights reserved.
//

#include "vector4.h"

const Vector4 Vector4::one (1, 1, 1, 1);

const Vector4 Vector4::zero (0, 0, 0, 0);

Vector4::Vector4 ()
: m_x (0)
, m_y (0)
, m_z (0)
, m_w (0)
{
    // do nothing
}

Vector4::Vector4 (GLfloat x, GLfloat y)
: m_x (x)
, m_y (y)
, m_z (0)
, m_w (0)
{
    // do nothing
}

Vector4::Vector4 (GLfloat x, GLfloat y, GLfloat z)
: m_x (x)
, m_y (y)
, m_z (z)
, m_w (0)
{
    // do nothing
}

Vector4::Vector4 (GLfloat x, GLfloat y, GLfloat z, GLfloat w)
: m_x (x)
, m_y (y)
, m_z (z)
, m_w (w)
{
    // do nothing
}

Vector4::~Vector4 ()
{
    // do nothing
}

GLfloat Vector4::GetMagnitude () const
{
    return sqrt (m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w);
}

GLfloat Vector4::GetSqrMagnitude () const
{
    return (m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w);
}

Vector4 Vector4::GetNormalized () const
{
    GLfloat magnitude = GetMagnitude ();
    return (*this) / magnitude;
}

GLfloat Vector4::GetX () const
{
    return m_x;
}

void Vector4::SetX (GLfloat x)
{
    m_x = x;
}

GLfloat Vector4::GetY () const
{
    return m_y;
}

void Vector4::SetY (GLfloat y)
{
    m_y = y;
}

GLfloat Vector4::GetZ () const
{
    return m_z;
}

void Vector4::SetZ (GLfloat z)
{
    m_z = z;
}

GLfloat Vector4::GetW () const
{
    return m_w;
}

void Vector4::SetW (GLfloat w)
{
    m_w = w;
}

void Vector4::Set (GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
    m_x = x;
    m_y = y;
    m_z = z;
    m_w = w;
}

GLfloat& Vector4::operator [] (int i)
{
    switch (i)
    {
        case 0: return m_x;
        case 1: return m_y;
        case 2: return m_z;
        case 3: return m_w;
        default: throw "Invalid index";
    }
}

const GLfloat& Vector4::operator[] (int i) const
{
    switch (i)
    {
        case 0: return m_x;
        case 1: return m_y;
        case 2: return m_z;
        case 3: return m_w;
        default: throw "Invalid index";
    }
}

Vector4 Vector4::operator - () const
{
    return Vector4 (-m_x, -m_y, -m_z, -m_w);
}

Vector4 Vector4::operator + (Vector4 v) const
{
    return Vector4 (m_x + v.GetX (), m_y + v.GetY (), m_z + v.GetZ (), m_w + v.GetW ());
}

Vector4 Vector4::operator - (Vector4 v) const
{
    return Vector4 (m_x - v.GetX (), m_y - v.GetY (), m_z - v.GetZ (), m_w - v.GetW ());
}

Vector4 Vector4::operator * (GLfloat s) const
{
    return Vector4 (m_x * s, m_y * s, m_z * s, m_w * s);
}

Vector4 Vector4::operator / (GLfloat s) const
{
    return Vector4 (m_x / s, m_y / s, m_z / s, m_w / s);
}

bool Vector4::operator == (Vector4 v) const
{
    return m_x == v.GetX () && m_y == v.GetY () && m_z == v.GetZ () && m_w == v.GetW ();
}

bool Vector4::operator != (Vector4 v) const
{
    return !((*this) == v);
}

std::ostream& operator << (std::ostream& stream, const Vector4& v)
{
     return stream << "[ x = " << v.m_x
                   << ", y = " << v.m_y
                   << ", z = " << v.m_z
                   << ", w = " << v.m_w << " ]";
}

GLfloat Vector4::Distance (Vector4 a, Vector4 b)
{
    Vector4 diff = a - b;
    return diff.GetMagnitude ();
}

GLfloat Vector4::Dot (Vector4 a, Vector4 b)
{
    return a.GetX () * b.GetX () + a.GetY () * b.GetY () + a.GetZ () * b.GetZ () + a.GetW () * b.GetW ();
}

Vector4 Vector4::Lerp (Vector4 from, Vector4 to, GLfloat t)
{
    Vector4 diff = to - from;
    return from + diff * t;
}

Vector4 Max (Vector4 a, Vector4 b)
{
    return Vector4 (fmax (a.GetX (), b.GetX ()),
                    fmax (a.GetY (), b.GetY ()),
                    fmax (a.GetZ (), b.GetZ ()),
                    fmax (a.GetW (), b.GetW ()));
}

Vector4 Min (Vector4 a, Vector4 b)
{
    return Vector4 (fmin (a.GetX (), b.GetX ()),
                    fmin (a.GetY (), b.GetY ()),
                    fmin (a.GetZ (), b.GetZ ()),
                    fmin (a.GetW (), b.GetW ()));
}

Vector4 MoveTowards (Vector4 current, Vector4 target, GLfloat maxDistanceDelta)
{
    Vector4 diff = target - current;
    if (maxDistanceDelta <= diff.GetMagnitude ())
    {
        return target;
    }
    else
    {
        return current + diff.GetNormalized () * maxDistanceDelta;
    }
}

Vector4 Project (Vector4 vector, Vector4 onNormal)
{
    Vector4 normal = onNormal.GetNormalized ();
    GLfloat magnitude = Vector4::Dot (vector, normal);
    return normal * magnitude;
}

Vector4 Scale (Vector4 a, Vector4 b)
{
    return Vector4 (a.GetX () * b.GetX (),
                    a.GetY () * b.GetY (),
                    a.GetZ () * b.GetZ (),
                    a.GetW () * b.GetW ());
}
